/**
 * Created by hjqi on 2016/1/13.
 */


var EventEmitter = require('events').EventEmitter;
var mongo_event = new EventEmitter();
var monk = require('monk');
var conf = require('../lib/config/config').config;
var logger = require('../lib/log4js/log4js').logger('myMongodb');
var g_db = monk(conf.mongodb.url,conf.mongodb.opts);
var SQL_TIMEOUT = (conf.mongodb.query_timeout === undefined?6000:conf.mongodb.query_timeout);
var reconn_retry = ((conf.mongodb.opts.server === undefined || conf.mongodb.opts.server.reconnectTries === undefined)?5:conf.mongodb.opts.server.reconnectTries);
var reconn_interval = ((conf.mongodb.opts.server === undefined || conf.mongodb.opts.server.reconnectInterval === undefined)?1000:conf.mongodb.opts.server.reconnectInterval);
var RESET_RECREATE_LOCK = reconn_retry*reconn_interval-500;
module.exports = myMongoDB;

function myMongoDB(collect_name){
    this.collect_name = collect_name;
    this.collection = g_db.get(collect_name,conf.mongodb.collectionOpts);
    this.lock = false;
    //setUpReCreateEvt();
}

myMongoDB.prototype.get = function (db,id,cb){
    if(id === null)
        this.collection.find({},cb);
    else
        this.collection.find({"id":id},cb);

}

myMongoDB.prototype.getWithParam = function (db,param,cb){
    var self = this;
    //var _timeout =setSqlTimeout(cb);
    this.collection.find(param,function(err,rets){
        //clearTimeout(_timeout);
        callbackExt(self,cb,err,rets);
        //cb(err,rets);
    });
}

myMongoDB.prototype.insert = function (db,meta,cb){
    var self = this;
    this.collection.insert(meta,function(err,rets){
        callbackExt(self,cb,err,rets);
    });
}

myMongoDB.prototype.updateOrInsert = function (db, meat, cb) {
    var self = this;
    this.collection.update({"username":meat.username},meat,{ upsert: true },function(err,rets){
        callbackExt(self,cb,err,rets);
    });
}

myMongoDB.prototype.del = function (db,id,cb){
    var self = this;
    this.collection.remove({"id":id},function(err,rets){
        callbackExt(self,cb,err,rets);
    });
}

myMongoDB.prototype.delWithParam = function (db,param,cb){
    var self = this;
    this.collection.remove(param,function(err,rets){
        callbackExt(self,cb,err,rets);
    });
}

myMongoDB.prototype.update = function (db,meta,cb){
    var self = this;
    this.collection.update({"id":meta.id},
                      meta,
                          { upsert: true },
        function(err,rets){
            callbackExt(self,cb,err,rets);
        });
}

myMongoDB.prototype.updateWithParam = function (db,param,meta,cb){
    var self = this;
    this.collection.update(param,
        meta,
        { upsert: true},
        function(err,rets){
            callbackExt(self,cb,err,rets);
        });
}

myMongoDB.prototype.updateMany = function (db,param,meta,cb){
    var self = this;
    this.collection.update(param,
        meta,
        { upsert: false,
          multi: true},
        function(err,rets){
            callbackExt(self,cb,err,rets);
        });
}

myMongoDB.prototype.updateField = function (db,param,meta,cb){
    var self = this;
    this.collection.update(param,
        {
            $set:meta
        },
        function(err,rets){
            callbackExt(self,cb,err,rets);
        });
}
myMongoDB.prototype.updateorInsertField = function (db,param,meta,cb){
    var self = this;
    this.collection.update(param,
        {
            $set:meta
        },
        { upsert: true},
        function(err,rets){
            callbackExt(self,cb,err,rets);
        });
}
myMongoDB.prototype.updateFieldExt = function (db,param,meta,cb){
    var self = this;
    this.collection.update(param,meta,function(err,rets){
        callbackExt(self,cb,err,rets);
    });
}

myMongoDB.prototype.ensureIndex = function (db,param,meta,cb){
    var self = this;
    this.collection.ensureIndex( param, meta, function(err,rets){
        callbackExt(self,cb,err,rets);
    }) ;
}

function setUpReCreateEvt(){

    mongo_event.on('reCreate',function(self){
        //if async many exceed reconnect times,let only one to recreate
        if(self.lock === false){
            self.lock = true;
            logger.info('db need to recreate new handler..');
            g_db.close();
            g_db = monk(conf.mongodb.url,conf.mongodb.opts);
            self.collection = g_db.get(self.collect_name,conf.mongodb.collectionOpts);

            //release lock after next reconnect period timeout
            setTimeout(function(){
                self.lock = false;
            },RESET_RECREATE_LOCK);

        }
    });
}

function isNeedToConnect(err){
    if(err.message.indexOf('failed to reconnect after') !== -1 ||
       err.message.indexOf('Topology was destroyed') !== -1 )
        return true;
    return false;
}
function callbackExt(self,cb,err,rets){
    if(err){
        if(isNeedToConnect(err))
            mongo_event.emit('reCreate',self);
    }

    cb(err,rets);
}

function setSqlTimeout(cb){
    return setTimeout(function(){
        cb(new Error('request mongo time out'),[]);
    },SQL_TIMEOUT);
}

setUpReCreateEvt();